# PS：笔记不要跑也不能跑，查就完事儿了

# 导入 selenium 中的 webdriver 模块  这样就可以进行web自动化了
from selenium import webdriver

# 导入定位元素的类
from selenium.webdriver.common.by import By

# 导入鼠标操作的类
from selenium.webdriver.common.action_chains import ActionChains

# 导入定位 selsct 选择框的类
from selenium.webdriver.support.ui import Select

# 显性等待模块
from selenium.webdriver.support.wait import WebDriverWait

# 导入 time 中 slssp 方法
# 此方法可以用于web自动化中的强制等待 --> 一直等待时间截止，如果没有达到预期则直接停止程序
from time import sleep


# 调用 Chrome 赋值一个对象，指定浏览器
web = webdriver.Chrome()


# 隐性等待
# 这是一个全局等待，只需要设置一个就可以
# 调用 implicitly_wait 方法 参数为以秒为单位 设置最多等待 x 秒
# 页面元素已加载，则不需要等待，如果页面没有找到元素则，每几毫秒查找一次，直至出现或者最大等待时长结束
web.implicitly_wait(1)


# 指定需要测试的网址
web.get('https://www.xxx.xxx.xx')

# 窗口最大化
web.maximize_window()

# 选择元素的方法
# find_element 选择一个符合条件的元素 如果没有，抛出异常
# find_elements 选择符合条件的所有元素  如果没有，返回空列表
# 1.根据 id 选择前端元素
web.find_element(By.ID, ('xxx\n'))  # \n --> 在此处是默认按下回车键

# 2.根据 name 选择元素
web.find_element(By.NAME, ('xxx'))

# 3.可以通过缩小范围去定位元素
# web 为整个页面，给在这个页面找出来的定位的元素赋值一个变量
# 在通过这个变量，去找 div 下的子元素
x = web.find_element(By.NAME, 'xxxx')
x.find_element(By.CLASS_NAME, 'xxxx')

# 4.根据 tag 名来定位元素
web.find_element(By.TAG_NAME, ('div'))

# 5.根据 CSS 表达式来定位元素
web.find_element(By.CSS_SELECTOR, '.xxx')  # CSS都是带. 的 记得带 .

# 6.CSS 表达式也可以来定位 id
web.find_element(By.CSS_SELECTOR, ('#id'))  # #id 前面要加 # 号

# 7.使用 > 来表达 CSS 表达式中父元素与子元素、后代元素的关系层，来达到缩小范围
web.find_element(By.CSS_SELECTOR, ("父元素>子元素>后代元素"))
web.find_element(By.CSS_SELECTOR, ('父元素>子元素>div'))  # 也可以混合写。表示子元素下所有的div

# 8.使用 空格 表达 CSS 表达式中的包含关系 不管 div 是否是父元素的子元素，都可以跨级定位
web.find_element(By.CSS_SELECTOR, ('父元素 div'))  # 同样可以混写

# 9.使用 , 号，表达 CSS 表达式中的两种条件同时满足
web.find_element(By.CSS_SELECTOR, '#id + .css div')

# 10.使用 + 号，表达 CSS 表达式中的兄弟元素关系
web.find_element(By.CSS_SELECTOR, 'h3 + span')  # 表示与 h3 同级一个span元素

# 11.使用 ~ 号，表达 CSS 表达式中的兄弟中所有的关系
web.find_element(By.CSS_SELECTOR, 'h3 ~ span')  # 表示与 h3 同级的所有span元素

# 12.使用 * 号，表达 CSS 表达式中的包含关系
# 表示 a 标签中，href属性的的值中包含 xxx
web.find_element(By.CSS_SELECTOR, 'a[href*="xxxx"]')

# 13.使用 ^ 号，表达 CSS 表达式中的开头关系
# 表示 div 标签中，name 属性的值开头为 hhtp
web.find_element(By.CSS_SELECTOR, 'div[name^="hhtp"]')

# 14.使用  & 号，表达 CSS 表达式中的结尾关系
# 表示 span 标签中，name 属性的值，结尾为 https
web.find_element(By.CSS_SELECTOR, 'span[name&="hhtps"]')

# 15.如果元素不存在 id class 等标签，也可以使用 CSS 表达式中的 [] 来定位
# 前端代码中怎么写，这就怎么写
# 如果 key 是当前网页唯一的，那么 value 可以不写
web.find_element(By.CSS_SELECTOR, '[key = value]')

# 16.使用 div:nth-child(x) 有序列的定位父元素下第 x 个同时又是 div 的子元素
# 查找父元素下第 2 个为 div 的元素
web.find_element(By.CSS_SELECTOR, 'div:nth-child(2)')
# 也可以使用 odd/even 来代表奇数和偶数
web.find_element(By.CSS_SELECTOR, 'div:nth-child(even)')
# 查找父元素下偶数个所有的 div 的元素

# 17.使用 div:nth-last-child(x) 有序列的定位父元素下倒数第 x 个同时又是 div 的子元素
# 查找父元素下倒数第 2 个为 div 的元素
web.find_element(By.CSS_SELECTOR, 'div:nth-last-child(2)')

# 18.使用 div:nth-fo-type(x) 有序列的定位父元素下第 x 个的 div 元素
# 定位父元素下第 2 个 div 标签
web.find_element(By.CSS_SELECTOR, 'div:nth-fo-type(2)')


# xpath 选择元素
# / 表示根目录 前后表示上下级关系 (类似绝对路径)
# // 双斜杠表示后代关系 //div --> 所有的div  (类似相对路径)
# * 号 代表类型

# 1.xpath 可以根据属性来选择元素
web.find_element(By.XPATH, '//*[@id=xx]')

# 2.xpath 中 使用 contains 来表达包含关系
web.find_element(By.XPATH, '//*[contains(@id="xxx")]')

# 3.xpath 中 使用 starts-with 来表示开头关系
web.find_element(By.XPATH, '//*[starts-with(@id="xx")]')

# 4.xpath 中 使用 ends-with 来表示结尾关系
web.find_element(By.XPATH, '//*[ends-with(@rel="xxx")]')

# 5.xpath 中 也可以按照元素次序选择
# 注意是 p 类型的第 2 个子元素 ！！
web.find_element(By.XPATH, '//p[2]')
web.find_element(By.XPATH, '//div/*[2]')    # div 下的第二个元素，不管什么元素

# 6.xpath 中 也可以倒序来选择元素
web.find_element(By.XPATH, '//p[last()]')   # p 标签中的倒数第一个
web.find_element(By.XPATH, '//p[last(-1)]')  # p 标签中的倒数第二个 以此类推

# 7.xpath 中的范围选择
# p 标签中索引 2 以内的 （既 1 和 2 ）
web.find_element(By.XPATH, '//p[position()<=2]')
web.find_element(By.XPATH, '//p[posinton()>=last()-2]')      # 倒数前两个

# 8.使用 | 来表示 xpath 中的 组选择
web.find_element(By.XPATH, '//p | //id')

# 9. /.. 来表示 xpanth 中的父节点选择
# 表示选择 id 为 xxx 元素的，父节点
web.find_element(By.XPATH, '//*[@id="xxx"]/..')

# 10.使用 following-sibling:: 来表达 xpath 中的后面兄弟节点
# 选择 class 属性为 a 的后面的所有兄弟节点
web.find_element(By.XPATH, '//*[@class="a"]/following-sibling::*')
# 选择class 属性为 a 的且后面为 div 的所有兄弟节点
web.find_element(By.XPATH, '//*[@class="a"]/following-sibling::div')

# 11.使用 preceing-sibling:: 来选择前面的兄弟节点
# 选择class 属性为 a 的且前面为 div 的所有兄弟节点
web.find_element(By.XPATH, '//*[@class="a"]/preceing-sibling::*')


# frame iframe 切换
# 使用 switch_to 中的 frame 方法
# 括号中的参数可以是 frame/iframe 的name 也可以按照序列，同时也可以写 CSS 表达式
web.switch_to.frame('div:nth-fo-type(2)')

# 使用 switch_to_default_content 切换回外层
web.switch_to_default_content()


# checkbox （多选）选择框
# 技巧：可以先把已经选择的，全部点击一遍，使其变成未选状态，在选择自己想要的元素
# 记住要用 find_elements 多选；使用 CSS 表达式中特殊的 [] 选择器
web.find_elements(By.CSS_SELECTOR, '[cheked="checked"]')


# selecet 下拉选择框 （需要先导入 select 类）
# 在创建一个select 对象
select = Select(web.find_element(By.ID, ('')))  # 注意，用什么选择方法都可以 这里用id

# 以下三种都是选中对象
# 1.根据html标签中的文本内容来选择
select.select_by_visible_text('xxxx')  # 填写 selsect 中的 text 属性的值即可

# 2.根据 value 的值来选择
select.select_by_value('xxx')

# 3.根据 次序 （从1开始）来选择
select.select_by_index('1')

# 以下四种则是，去除 选中效果
# 1.根据html标签中的文本内容来选择去除
select.deselect_by_visible_text()

# 2.根据 value 的值来选择去除
select.deselect_by_value('xxx')

# 3.根据 次序 （从1开始）来选择去除
select.deselect_by_index('1')

# 4.去除 所有选中的元素
select.deselect_all('')  # 最为常用


# 窗口切换
# 可以提前使用 current_window_handle 把当前句柄存储到一个变量中，方便之后返回原窗口
handle = web.current_window_handle

# 获取当前页面的 URL 存储到变量中
handle1 = web.current_url

# 储存了原窗口的情况下,直接 switch_to.window + 变量名即可
web.switch_to.window(handle1)

# 未储存原窗口的情况，可以是用循环
# 使用 window 方法 返回当前浏览器中所有的 handle（句柄）
web.switch_to.window()

# 使用for循环遍历每个句柄，并且获取 头部 ulr也可以
for jubing in web.title:
    # 判断每个 头部，是否符合要跳转的窗口
    if 'xxx' in web.title:
        # True 则 跳出循环
        break


# 弹出框处理 (以下弹窗的情况，均为web浏览器原生的提示，非原生直接使用 css 选择器即可)
# 1. akert
# 打印 弹出框 提示信息
web.find_element(By.CLASS_NAME, 'xxxxx').click()    # 定位元素
akert = web.switch_to.alert     # 获取弹框内容
print(akert.text)   # 打印出来

# 使用 caaept() 方法点击 确定 按钮
web.switch_to.alert.accept()

# 2.存在 取消 的提示框
# 使用 dismiss() 方法，可以点击取消提示框
web.switch_to.alert.dismiss()

# 3.使用 send_keys() 在弹框中输入信息
# 原生框中，一定要执行完点击确定步骤，才会显示内容，单部调试无法看到
web.switch_to.send_keys('sssss')
web.switch_to.accept()


# 鼠标的操作
# 需要导入 ActionChains 类，并且创建一个实例对象 传入webdriver对象
mouse = ActionChains(web)

# 对 mouse 操作 同样也需要先定位元素，此处不再赘述 直接介绍方法
y = web.find_element(By.CSS_SELECTOR, 'xxx')  # 定位并且赋值变量

# 对鼠标的操作步骤一定要调用 perform() 方法 否则无法执行
# 1.单击右键
mouse.context_click(y)

# 2.双击 与上一个 单击操作 为两种写法
ActionChains(web).double_click(
    web.find_element(By.CLASS_NAME, 'xxxxx')).perform()

# 3.鼠标悬停
mouse.move_to_element(
    web.find_element(By.CSS_SELECTOR, 'xxxxx')).perform()

# 4.拖动
# 此操作需要两个必要参数
source = web.find_element(By.ID, 'xx')   # 要拖动的元素
target = web.find_element(By.CLASS_NAME, 'xxx')  # 拖到的位置
ActionChains(web).drag_and_drop(source, target).perform()  # 执行

# 5.冻结页面
# 此方法便于去前端获取元素
# 在谷歌浏览器中的Console中写入如下代码
# setTimeout(function(){debugger},5000) --> 5000毫秒后，冻结页面 此代码为JS的代码不是Python的


# 常规操作
# 进行操作时，需要赋值变量，否则无法调用操作方法
# 1.输入指定内容
x.send_keys('阿巴阿巴阿巴')

# 2.清除文本内容
x.clear()

# 3.使用 title 打印当前窗口的标题栏
# webdriver 对象 才可以调用 title
print(web.title)

# 打印元素属性
# 需要给指定元素赋值一个变量，否则无法调用
print(x.get_attribute())

# 1.可以填入参数 outerHTML 获取所有的html
print(x.get_attribute('outerHTML'))

# 2.可以填入参数 value 获取 input 输入框里面的文字
print(x.get_attribute('value'))

# 3.可以填入参数 text 获取展示在界面上的文本内容
# 如果出现问题则，则填入参数 innrText 或 textContent
print(x.get_attribute('text'))

# 关闭浏览器
# 1.quit() 关闭浏览器，并且关闭浏览器的后台进程
# 2.close() 关闭浏览器窗口
web.quit()
